// PanelItems.cpp

#include "StdAfx.h"
#include "Panel.h"

#include "Common/MS/PropVariant.h"

#include "Common/7Zip/PropID.h"

#include "FileManager/Windows.h"

using namespace Common;



static int GetColumnAlign(PROPID propID, VARTYPE varType)
{
	switch(propID)
	{
		case kpidCTime:
		case kpidATime:
		case kpidMTime:
			return LVCFMT_LEFT;
	}
	switch(varType)
	{
		case VT_UI1:
		case VT_I2:
		case VT_UI2:
		case VT_I4:
		case VT_INT:
		case VT_UI4:
		case VT_UINT:
		case VT_I8:
		case VT_UI8:
		case VT_BOOL:
			return LVCFMT_RIGHT;

		case VT_EMPTY:
		case VT_I1:
		case VT_FILETIME:
		case VT_BSTR:
			return LVCFMT_LEFT;

		default:
			return LVCFMT_CENTER;
	}
}

HRESULT CPanel::InitColumns()
{
	if (_needSaveInfo)
	{
		SaveListViewInfo();
	}

	_listView.DeleteAllItems();
	_selectedStatusVector.clear();

	ReadListViewInfo();


	PROPID sortID = _listViewInfo.SortID;

	_ascending = _listViewInfo.Ascending;

	m_directory.ResetProperties(_listViewInfo);
	
	_needSaveInfo = true;

	for (;;)
		if (!_listView.DeleteColumn(0))
		{
			break;
		}

	_sortID = _listViewInfo.SortID;

	for (size_t i = 0; i < m_directory.GetVisibleProperties().size(); i++)
	{
		InsertColumn(i);
	}
	return S_OK;
}

void CPanel::InsertColumn(int index)
{
	const auto &prop = m_directory.GetVisibleProperties()[index];
	LV_COLUMNW column;
	column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
	column.cx = prop.Width;
	column.fmt = GetColumnAlign(prop.ID, prop.Type);
	column.iOrder = prop.Order;
	column.iSubItem = index;
	column.pszText = const_cast<wchar_t*>((const wchar_t*)prop.Name);
	_listView.InsertColumn(index, &column);
}

HRESULT CPanel::RefreshListCtrl()
{
	return RefreshListCtrl(CString(), -1, true, CStringVector());
}

int CALLBACK CompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData);


void CPanel::GetSelectedNames(CStringVector &selectedNames)
{
	selectedNames.clear();

	std::vector<UINT32> indices;
	GetSelectedItemsIndices(indices);
	//selectedNames.reserve(indices.size());
	for (size_t  i = 0; i < indices.size(); i++)
	{
		selectedNames.push_back(m_directory.GetItemRelPath(indices[i]));
	}

	std::sort(selectedNames.begin(), selectedNames.end());
}

void CPanel::SaveSelectedState(CSelectedState &s)
{
	s.FocusedName.Empty();
	s.SelectedNames.clear();
	s.FocusedItem = _listView.GetNextItem(-1, LVNI_FOCUSED);
	{
		if (s.FocusedItem >= 0)
		{
			int realIndex = GetRealItemIndex(s.FocusedItem);
			if (realIndex != FileManager::FileSystem::kParentIndex)
			{
				s.FocusedName = m_directory.GetItemRelPath(realIndex);
			}
		}
	}
	GetSelectedNames(s.SelectedNames);
}

HRESULT CPanel::RefreshListCtrl(const CSelectedState &s)
{
	bool selectFocused = s.SelectFocused;
	if (_mySelectMode)
	{
		selectFocused = true;
	}
	return RefreshListCtrl(s.FocusedName, s.FocusedItem, selectFocused, s.SelectedNames);
}

HRESULT CPanel::RefreshListCtrlSaveFocused()
{
	CSelectedState state;
	SaveSelectedState(state);
	return RefreshListCtrl(state);
}

void CPanel::SetFocusedSelectedItem(int index, bool select)
{
	UINT state = LVIS_FOCUSED;
	if (select)
	{
		state |= LVIS_SELECTED;
	}
	_listView.SetItemState(index, state, state);
	if (!_mySelectMode && select)
	{
		int realIndex = GetRealItemIndex(index);
		if (realIndex != FileManager::FileSystem::kParentIndex)
		{
			_selectedStatusVector[realIndex] = true;
		}
	}
}

HRESULT CPanel::RefreshListCtrl(const CString &focusedName, int focusedPos, bool selectFocused,
								const CStringVector &selectedNames)
{
	_dontShowMode = false;
	LoadFullPathAndShow();
	// OutputDebugStringA("=======\n");
	// OutputDebugStringA("s1 \n");
	CDisableTimerProcessing timerProcessing(*this);

	if (focusedPos < 0)
	{
		focusedPos = 0;
	}

	_listView.SetRedraw(false);

	LVITEM item;
	ZeroMemory(&item, sizeof(item));

	_listView.DeleteAllItems();
	_selectedStatusVector.clear();
	_startGroupSelect = 0;

	_selectionIsDefined = false;


	if (!m_directory.GetIFolder())
	{
		// throw 1;
		m_directory.SetToRootFolder();
	}

	_headerToolBar.EnableButton(kParentFolderID, !m_directory.IsRootFolder());

	HRESULT hRes(m_directory.GetIFolder()->LoadItems());
	if(S_OK != hRes)
	{
		return hRes;
	}
	hRes = InitColumns();
	if(S_OK != hRes)
	{
		return hRes;
	}

	// OutputDebugString(TEXT("Start Dir\n"));
	UINT32 numItems = m_directory.GetNumberOfItems() ;
	
	bool showDots = _showDots && !m_directory.IsRootFolder();

	_listView.SetItemCount(numItems + (showDots ? 1 : 0));

	_selectedStatusVector.reserve(numItems);
	int cursorIndex = -1;

	CComPtr<IFolderGetSystemIconIndex> folderGetSystemIconIndex;
	if (!m_directory.IsFSFolder() || _showRealFileIcons)
	{
		m_directory.GetIFolder()->QueryInterface(__uuidof(IFolderGetSystemIconIndex), (void**)&folderGetSystemIconIndex);
	}

	if (showDots)
	{
		CString itemName = _T("..");
		item.iItem = _listView.GetItemCount();
		if (itemName.CompareNoCase(focusedName) == 0)
		{
			cursorIndex = item.iItem;
		}
		item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
		int subItem = 0;
		item.iSubItem = subItem++;
		item.lParam = FileManager::FileSystem::kParentIndex;
		item.pszText = itemName.GetBuffer(); //const_cast<wchar_t *>((const wchar_t *)itemName);
		UINT32 attrib = FILE_ATTRIBUTE_DIRECTORY;
		item.iImage = _extToIconMap.GetIconIndex(attrib, itemName);
		if (item.iImage < 0)
		{
			item.iImage = 0;
		}
		if (_listView.InsertItem(&item) == -1)
		{
			return E_FAIL;
		}
	}

	// OutputDebugStringA("S1\n");

	for (UINT32 i = 0; i < numItems; i++)
	{
		CString itemName = m_directory.GetItemName(i);
		const CString relPath = m_directory.GetItemRelPath(i);
		if (relPath.CompareNoCase(focusedName) == 0)
		{
			cursorIndex = _listView.GetItemCount();
		}
		bool selected = false;
		CStringVector::const_iterator it = std::find(selectedNames.begin(), selectedNames.end(), relPath);
		if (it != selectedNames.end())
		{
			selected = true;
		}
		_selectedStatusVector.push_back(selected);

		item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;

		if (!_mySelectMode)
			if (selected)
			{
				item.mask |= LVIF_STATE;
				item.state = LVIS_SELECTED;
			}

		int subItem = 0;
		item.iItem = _listView.GetItemCount();

		item.iSubItem = subItem++;
		item.lParam = i;

		CString correctedName;
		if (itemName.Find(_T("     ")) >= 0)
		{
			int pos = 0;
			for (;;)
			{
				int posNew = itemName.Find(_T("     "), pos);
				if (posNew < 0)
				{
					correctedName += itemName.Mid(pos);
					break;
				}
				correctedName += itemName.Mid(pos, posNew - pos);
				correctedName += _T(" ... ");
				pos = posNew;
				while (itemName[++pos] == ' ');
			}
			item.pszText = const_cast<wchar_t*>((const wchar_t*)correctedName);
		}
		else
		{
			item.pszText = const_cast<wchar_t*>((const wchar_t*)itemName);
		}

		CComPropVariant prop;
		hRes = m_directory.GetIFolder()->GetProperty(i, kpidAttrib, &prop);
		if(S_OK != hRes)
		{
			return hRes;
		}
		UINT32 attrib = 0;
		if (prop.vt == VT_UI4)
		{
			attrib = prop.ulVal;
		}
		else if (m_directory.IsItemFolder(i))
		{
			attrib |= FILE_ATTRIBUTE_DIRECTORY;
		}

		bool defined  = false;

		if (folderGetSystemIconIndex)
		{
			folderGetSystemIconIndex->GetSystemIconIndex(i, &item.iImage);
			defined = (item.iImage > 0);
		}
		if (!defined)
		{
			if (m_directory.GetCurrentDirectory().IsEmpty())
			{
				int iconIndexTemp;
				Common::FileSystem::GetRealIconIndex(itemName + CHAR_PATH_SEPARATOR, attrib, iconIndexTemp);
				item.iImage = iconIndexTemp;
			}
			else
			{
				item.iImage = _extToIconMap.GetIconIndex(attrib, itemName);
			}
		}
		if (item.iImage < 0)
		{
			item.iImage = 0;
		}

		if (_listView.InsertItem(&item) == -1)
		{
			return E_FAIL;    // error
		}
	}
	// OutputDebugStringA("End2\n");

	if (_listView.GetItemCount() > 0 && cursorIndex >= 0)
	{
		SetFocusedSelectedItem(cursorIndex, selectFocused);
	}
	_listView.SortItems(CompareItems, (LPARAM)this);
	if (cursorIndex < 0 && _listView.GetItemCount() > 0)
	{
		if (focusedPos >= _listView.GetItemCount())
		{
			focusedPos = _listView.GetItemCount() - 1;
		}
		// we select item only in showDots mode.
		SetFocusedSelectedItem(focusedPos, showDots);
	}
	_listView.EnsureVisible(_listView.GetNextItem(-1, LVNI_FOCUSED), false);
	_listView.SetRedraw(true);
	_listView.InvalidateRect(NULL, true);
	// OutputDebugStringA("End1\n");
	return S_OK;
}

void CPanel::GetSelectedItemsIndices(std::vector<UINT32> &indices) const
{
	indices.clear();

	for (size_t i = 0; i < _selectedStatusVector.size(); i++)
		if (_selectedStatusVector[i])
		{
			indices.push_back(i);
		}
	std::sort(indices.begin(), indices.end());
}

void CPanel::GetOperatedItemIndices(std::vector<UINT32> &indices) const
{
	GetSelectedItemsIndices(indices);
	if (!indices.empty())
	{
		return;
	}
	if (_listView.GetSelectedCount() == 0)
	{
		return;
	}
	int focusedItem = _listView.GetNextItem(-1, LVNI_FOCUSED);
	if (focusedItem >= 0)
	{
		if (_listView.GetItemState(focusedItem, LVIS_SELECTED) == LVIS_SELECTED)
		{
			int realIndex = GetRealItemIndex(focusedItem);
			if (realIndex != FileManager::FileSystem::kParentIndex)
			{
				indices.push_back(realIndex);
			}
		}
	}
}

void CPanel::GetAllItemIndices(std::vector<UINT32> &indices) const
{
	indices.clear();
	UINT32 numItems = m_directory.GetNumberOfItems() ;
	for (UINT32 i = 0; i < numItems; i++)
	{
		indices.push_back(i);
	}
}

void CPanel::GetOperatedIndicesSmart(std::vector<UINT32> &indices) const
{
	GetOperatedItemIndices(indices);
	if (indices.empty() || (indices.size() == 1 && indices[0] == (UINT32)(INT32) - 1))
	{
		GetAllItemIndices(indices);
	}
}

void CPanel::EditItem()
{
	int focusedItem = _listView.GetNextItem(-1, LVNI_FOCUSED);
	if (focusedItem < 0)
	{
		return;
	}
	int realIndex = GetRealItemIndex(focusedItem);
	if (realIndex == FileManager::FileSystem::kParentIndex)
	{
		return;
	}
	if (m_directory.EditItem(realIndex))
	{
		RefreshListCtrl();
	}
}

void CPanel::OpenFocusedItemAsInternal()
{
	int focusedItem = _listView.GetNextItem(-1, LVNI_FOCUSED);
	if (focusedItem < 0)
	{
		return;
	}
	int realIndex = GetRealItemIndex(focusedItem);
	if (m_directory.IsItemFolder(realIndex))
	{
		m_directory.OpenFolder(realIndex);
	}
	else
	{
		m_directory.OpenItem(realIndex, true, false);
	}
}

void CPanel::OpenSelectedItems(bool tryInternal)
{
	std::vector<UINT32> indices;
	GetOperatedItemIndices(indices);
	if (indices.size() > 20)
	{
		FileManager::CWindows::MessageBoxErrorLang(*this, IDS_TOO_MANY_ITEMS, 0x02000606);
		return;
	}

	int focusedItem = _listView.GetNextItem(-1, LVNI_FOCUSED);
	if (focusedItem >= 0)
	{
		int realIndex = GetRealItemIndex(focusedItem);
		if (realIndex == FileManager::FileSystem::kParentIndex && 
				(tryInternal || indices.size() == 0) &&
				_listView.GetItemState(focusedItem, LVIS_SELECTED) == LVIS_SELECTED)
		{
			indices.insert(indices.begin(), realIndex);
		}
	}

	bool dirIsStarted = false;
	for (size_t i = 0; i < indices.size(); i++)
	{
		UINT32 index = indices[i];
		if (m_directory.IsItemFolder(index))
		{
			if (!dirIsStarted)
			{
				if (tryInternal)
				{
					m_directory.OpenFolder(index);
					dirIsStarted = true;
					break;
				}
				else
				{
					m_directory.OpenFolderExternal(index);
				}
			}
		}
		else
		{
			m_directory.OpenItem(index, (tryInternal && indices.size() == 1), true);
		}
	}
	RefreshListCtrl();
}


void CPanel::ReadListViewInfo()
{
	_typeIDString = m_directory.GetFolderTypeID();
	if (!_typeIDString.IsEmpty())
	{
		::ReadListViewInfo(_typeIDString, _listViewInfo);
	}
}

void CPanel::SaveListViewInfo()
{
	size_t i;
	for (i = 0; i < m_directory.GetVisibleProperties().size(); i++)
	{
		auto & prop = m_directory.GetVisibleProperties()[i];
		LVCOLUMN winColumnInfo;
		winColumnInfo.mask = LVCF_ORDER | LVCF_WIDTH;
		if (!_listView.GetColumn(i, &winColumnInfo))
		{
			throw 1;
		}
		prop.Order = winColumnInfo.iOrder;
		prop.Width = winColumnInfo.cx;
	}

	CListViewInfo viewInfo;

	PROPID sortPropID = _sortID;


	
	for (const auto & prop : m_directory.GetVisibleProperties())
	{
		CColumnInfo columnInfo;
		columnInfo.IsVisible = prop.IsVisible;
		columnInfo.PropID = prop.ID;
		columnInfo.Width = prop.Width;
		viewInfo.Columns.push_back(columnInfo);
	}
	for (const auto & prop : m_directory.GetAllProperties())
	{
		if (!prop.IsVisible)
		{
			CColumnInfo columnInfo;
			columnInfo.IsVisible = prop.IsVisible;
			columnInfo.PropID = prop.ID;
			columnInfo.Width = prop.Width;
			viewInfo.Columns.push_back(columnInfo);
		}
	}

	viewInfo.SortID = sortPropID;

	viewInfo.Ascending = _ascending;
	if (!_listViewInfo.IsEqual(viewInfo))
	{
		::SaveListViewInfo(_typeIDString, viewInfo);
		_listViewInfo = viewInfo;
	}
}


bool CPanel::OnRightClick(MY_NMLISTVIEW_NMITEMACTIVATE* itemActiveate, LRESULT &result,  BOOL &bHandled)
{
	if (itemActiveate->hdr.hwndFrom == HWND(_listView))
	{
		return false;
	}
	CPoint point;
	::GetCursorPos(&point);
	ShowColumnsContextMenu(point);
	result = TRUE;
	return true;
}

void CPanel::ShowColumnsContextMenu(CPoint point)
{

	CMenu menu;

	menu.CreatePopupMenu();

	const int kCommandStart = 100;
	auto & allProps = m_directory.GetAllProperties();
	auto & visibleProps = m_directory.GetAllProperties();
	for (size_t i = 0; i < allProps.size(); i++)
	{
		const auto &prop = m_directory.GetAllProperties()[i];
		UINT flags =  MF_STRING;
		if (prop.IsVisible)
		{
			flags |= MF_CHECKED;
		}
		if (i == 0)
		{
			flags |= MF_GRAYED;
		}
		menu.AppendMenu(flags, kCommandStart + i, prop.Name);
	}
	UINT32 menuResult = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY, point.x, point.y, _listView);
	if (menuResult >= kCommandStart && menuResult <= kCommandStart + allProps.size())
	{
		int index = menuResult - kCommandStart;
		auto &prop = allProps[index];
		prop.IsVisible = !prop.IsVisible;

		if (prop.IsVisible)
		{
			size_t prevVisibleSize = visibleProps.size();
			prop.Order = prevVisibleSize;
			visibleProps.push_back(prop);
			InsertColumn(prevVisibleSize);
		}
		else
		{
			int visibleIndex = visibleProps.FindItemWithID(prop.ID);
			visibleProps.erase(visibleProps.begin() + visibleIndex);

			if (_sortID == prop.ID)
			{
				_sortID = kpidName;
				_ascending = true;
			}

			_listView.DeleteColumn(visibleIndex);
		}
	}
}

void CPanel::OnReload()
{
	HRESULT res = RefreshListCtrlSaveFocused();
	if (res != S_OK)
	{
		FileManager::CWindows::MessageBoxError(*this, res);
	}
	OnRefreshStatusBar();
}

void CPanel::OnTimer(UINT_PTR nIDEvent)
{
	if (!_processTimer || m_directory.GetProcessTimer())
	{
		return;
	}
	if (!m_directory.FolderHasChanged())
		return;
	OnReload();
}
